New Inbound mail is live: replies arrive as JSON.

Send the message. Skip the plumbing.

Transactional, broadcast, and automated email behind one API. Priced by volume, not contacts — and your agents can send too.

A painted alpine valley at dusk, a river winding toward the mountains

Send a welcome email to everyone who signed up today, from Mara, with a link to the quickstart.

Ask Herald to send…

From Mara at Herald · To 128 people

Welcome to Herald

Your first send is three lines of code away.

  1. Queued 0.2s
  2. Sent 1.1s
  3. Delivered 1.9s
  • Send API

    One POST to send. Templates, attachments, idempotency keys, and a delivery event for every message.

    Docs
  • MCP server

    Give your agent tools to send, draft campaigns, manage contacts, and read delivery stats.

    Docs
  • Agent skill

    A SKILL.md your coding agent reads once and gets Herald right on the first try.

    Docs
3,417,905
Sent, past 7 days
98.61%
Delivery rate
1.42s
Time to inbox
0.87%
Bounce rate

Sent by teams of one and teams of one thousand

  • Orbital
  • Pine Labs
  • Looply
  • Artifact

#01 – Transactional mail

Codes, receipts, and alerts your users can count on.

  • One POST, or point your SMTP client at us. Templates, attachments, tags, and an idempotency key so a retried request never sends twice.

  • Typed clients for Node, Python, PHP, Go, Ruby, and Rust — with the same names as the API, so the docs and the autocomplete agree.

  • Point an address at Herald and receive every reply as structured JSON: parsed body, attachments, headers, and a verified sender.

  • A signed event for every delivery, open, click, bounce, and complaint, retried with backoff until you return a 200.

A painted mountain lake at blue hour
Send a message
curl https://api.herald.dev/v1/send \
  -H "Authorization: Bearer $HERALD_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Mara <mara@herald.dev>",
    "to": "sam@acme.com",
    "subject": "Welcome to Herald",
    "template": "welcome",
    "data": { "name": "Sam" }
  }'
import { Herald } from 'herald';

const herald = new Herald(process.env.HERALD_KEY);

await herald.send({
  from: 'Mara <mara@herald.dev>',
  to: 'sam@acme.com',
  subject: 'Welcome to Herald',
  template: 'welcome',
  data: { name: 'Sam' },
});
from herald import Herald

herald = Herald(os.environ["HERALD_KEY"])

herald.send(
    from_="Mara <mara@herald.dev>",
    to="sam@acme.com",
    subject="Welcome to Herald",
    template="welcome",
    data={"name": "Sam"},
)
$herald = new Herald\Client(getenv('HERALD_KEY'));

$herald->send([
    'from' => 'Mara <mara@herald.dev>',
    'to' => 'sam@acme.com',
    'subject' => 'Welcome to Herald',
    'template' => 'welcome',
    'data' => ['name' => 'Sam'],
]);
client := herald.New(os.Getenv("HERALD_KEY"))

_, err := client.Send(ctx, herald.Message{
    From:     "Mara <mara@herald.dev>",
    To:       "sam@acme.com",
    Subject:  "Welcome to Herald",
    Template: "welcome",
    Data:     map[string]any{"name": "Sam"},
})
herald = Herald::Client.new(ENV["HERALD_KEY"])

herald.send(
  from: "Mara <mara@herald.dev>",
  to: "sam@acme.com",
  subject: "Welcome to Herald",
  template: "welcome",
  data: { name: "Sam" }
)
Send a message
curl https://api.herald.dev/v1/send \
  -H "Authorization: Bearer $HERALD_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Mara <mara@herald.dev>",
    "to": "sam@acme.com",
    "subject": "Welcome to Herald",
    "template": "welcome",
    "data": { "name": "Sam" }
  }'
import { Herald } from 'herald';

const herald = new Herald(process.env.HERALD_KEY);

await herald.send({
  from: 'Mara <mara@herald.dev>',
  to: 'sam@acme.com',
  subject: 'Welcome to Herald',
  template: 'welcome',
  data: { name: 'Sam' },
});
from herald import Herald

herald = Herald(os.environ["HERALD_KEY"])

herald.send(
    from_="Mara <mara@herald.dev>",
    to="sam@acme.com",
    subject="Welcome to Herald",
    template="welcome",
    data={"name": "Sam"},
)
$herald = new Herald\Client(getenv('HERALD_KEY'));

$herald->send([
    'from' => 'Mara <mara@herald.dev>',
    'to' => 'sam@acme.com',
    'subject' => 'Welcome to Herald',
    'template' => 'welcome',
    'data' => ['name' => 'Sam'],
]);
client := herald.New(os.Getenv("HERALD_KEY"))

_, err := client.Send(ctx, herald.Message{
    From:     "Mara <mara@herald.dev>",
    To:       "sam@acme.com",
    Subject:  "Welcome to Herald",
    Template: "welcome",
    Data:     map[string]any{"name": "Sam"},
})
herald = Herald::Client.new(ENV["HERALD_KEY"])

herald.send(
  from: "Mara <mara@herald.dev>",
  to: "sam@acme.com",
  subject: "Welcome to Herald",
  template: "welcome",
  data: { name: "Sam" }
)
Inbound · parsed
from
sam@acme.com
subject
Re: Your invoice for March
reply
Paid this morning, thanks.
attachments
[]
in_reply_to
msg_8f2k
auth
SPF · DKIM · DMARC
Delivery log
  • sam@acme.com Delivered 1.1s
  • j.lee@northwind.io Delivered 0.9s
  • riley@marsh.studio Delivered 1.4s
  • ops@frostline.io Deferred 12.0s
  • old-address@example.org Bounced 0.3s
  • hello@looply.app Delivered 1.2s
  • team@orbital.dev Delivered 1.0s

#02 – Broadcasts

Newsletters and launches that land in the inbox, not the tab.

  • Write a broadcast, pick a segment, schedule it. Newsletters, launches, and the occasional apology all go out the same way.

  • Herald ramps a new domain over three weeks so mailbox providers learn to trust it before you send your first big list.

  • Filter contacts by property, event, or engagement, and the segment updates itself as people qualify or fall out.

  • Write in markdown, preview in every major client, and ship a message that renders the same in Gmail and Outlook.

Painted moorland hills at dawn

Campaigns

Broadcasts to a segment of your contacts.

  • Sending 24 Mar, 2026 · 10:24

    Inbound is live

    Subject: Replies now arrive as JSON

  • Sent 20 Mar, 2026 · 09:40

    March notes

    Subject: What we shipped, and one thing we broke

  • Sent 12 Mar, 2026 · 08:15

    Warmup week three

    Subject: Your domain is ready for the big list

  • Draft Not scheduled

    Pricing update

    Subject: A smaller bill for most of you

Domain warmup
  • 1
  • 3
  • 5
  • 7
  • 9
  • 11
  • 13
  • 14

Day 14 of 21 · cap doubles while bounces stay low

Segment · Active on Pro
  • Where plan is pro
  • And last_seen within 30 days
  • And emails_opened at least 3

2,410 contacts match right now

Editor · March notes

# What we shipped

Inbound mail is live. Point an address at Herald and every reply arrives as JSON.

[Read the changelog](/changelog)

What we shipped

Inbound mail is live. Point an address at Herald and every reply arrives as JSON.

Read the changelog

#03 – Automations

Sequences that send themselves, from signup to renewal.

  • Welcome series, onboarding nudges, and win-back campaigns that run themselves once a contact enters them.

  • Start a sequence from a signup, a property change, or an event your product sends, and stop it the moment they convert.

  • Opens, clicks, and conversions per step, so you can see which message does the work and which one gets skipped.

A painted alpine valley at dusk
  1. Trigger

    Contact is added to New signups

  2. Welcome to Herald From Mara <mara@herald.dev>
  3. Wait for 2 days

  4. Your first send, step by step Skipped if they have already sent
  5. Wait for 5 days

  6. Sequence ends

    When every email has gone out, or the moment the contact upgrades.

Triggers
  • contact.created Starts Welcome series
  • plan.changed → pro Starts Getting the most from Pro
  • invoice.overdue Starts Payment reminders
  • contact.inactive_30d Starts Win-back
Analytics · last 30 days
  • Delivered 98.6% +0.4

  • Opened 61.2% +3.1

  • Clicked 18.7% +1.9

  • Upgraded 4.3% +0.8

Workspaces

Every product, one bill.

Create a workspace for each product, client, or environment you run, each with its own domains, contacts, templates, and API keys — and one invoice at the end of the month.

Painted moorland hills at dawn with a chalk path over the ridge
Acme
  • Acme mail.acme.com
  • Frostline frostline.io
  • Staging staging.acme.com
  • New workspace
  • We moved 190,000 users off a contact-priced plan in an afternoon. The bill went down by two thirds and nobody noticed the switch.

    Sam Okafor Founder, Orbital Orbital
  • The migration took a morning. The team answered every question the same day, and the product does what the bigger names do without the bigger invoice.

    Jordan Lee Engineering lead, Pine Labs Pine Labs
  • Our agents send receipts and alerts on their own now. The MCP tools and the skill file mean they get the send right without a human reviewing every call.

    Riley Marsh CTO, Looply Looply

Ask around.

See what your favourite model says about Herald, then decide for yourself.